import time

from apscheduler.schedulers.blocking import BlockingScheduler

from database.mapper import stock_info, stock_day_k, stock_day_macd, stock_monitor, stock_month_k, stock_month_macd, \
    stock_day_info, boards_info
from service import get_all_plates_info_v1 as gapi_v1, get_gp_funds_info_v1 as ggfi_v1, diagnosis_one_key as dok, \
    log_service
from task import c4 as task_c
from utils import date_util, k_util_v1, macd_util_v1, caiptal_util, cache_util, parse_util, msg_util, examples
from webs.service import web_service

miao_code = 'tOCarr1'


# 数据刷新定时任务
def fresh_data():
    # 添加任务,时间间隔30S
    scheduler = BlockingScheduler()
    # 从开始时间到结束时间，每隔一小时运行一次
    scheduler.add_job(fresh_task_interval, 'interval', seconds=3600, id='monitor_task')
    # 自有应用缓存
    scheduler.add_job(fresh_cache, 'interval', seconds=180, id='cache_task')
    # 开市前数据准备
    scheduler.add_job(prepare_data, 'cron', day_of_week='0-4', hour=9, minute=10)
    # 开市插入新数据
    scheduler.add_job(add_new_task, 'cron', day_of_week='0-4', hour=9, minute=31)
    # 闭市再次刷新数据
    scheduler.add_job(fresh_task_close, 'cron', day_of_week='0-4', hour=15, minute=30)
    # 刷新日信息
    scheduler.add_job(fresh_day_info, 'cron', day_of_week='0-4', hour=15, minute=20)
    # 刷新月信息
    scheduler.add_job(fresh_month_info, 'cron', day_of_week='0-4', hour=16, minute=00)
    # 刷新诊断信息(80分)
    # scheduler.add_job(fresh_diagnosis_info, 'cron', day_of_week='0-4', hour=16, minute=30)
    # task_c 监控所有的股票
    scheduler.add_job(task_c_all, 'cron', day_of_week='0-4', hour=17, minute=00)
    # 基金和板块信息刷新
    scheduler.add_job(fresh_fund_board_info, 'cron', day_of_week='5', hour=10, minute=30)
    # 根据实例监控
    scheduler.add_job(monitor_examples, 'cron', day_of_week='5', hour=11, minute=30)
    scheduler.start()


# 根据实例监控
def monitor_examples():
    examples.find()


# task_c所有
def task_c_all():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    msg_util.miao_msg(miao_code, '定时任务-开始刷新task_c开始')
    task_c.monitor_all()
    msg_util.miao_msg(miao_code, '定时任务-开始刷新task_c结束')


# 准备非实时数据
def prepare_data():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    # 刷新排行榜
    web_service.rank_mio_task(True)
    web_service.board_info_task(True)
    msg_util.miao_msg(miao_code, '定时任务-准备数据完成')


# 开市插入新数据
# 已完成
def add_new_task():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    msg_util.miao_msg(miao_code, '定时任务-开始刷新数据开始')
    s_list = stock_info.selectall()
    # 增加日K
    add_day_k(s_list)
    # 增加日macd
    add_day_macd(s_list)
    msg_util.miao_msg(miao_code, '定时任务-开始刷新数据结束')


# 增加day_k
def add_day_k(s_list):
    for s in s_list:
        code = s['code']
        day_k = stock_day_k.select(code)
        if len(day_k) == 0:
            k_util_v1.init_day_k(code)
        else:
            o_rq = day_k['rq']
            d_rq = date_util.str_to_date(date_util.get_date_str(), '%Y-%m-%d')
            diff_d = date_util.diff_days(o_rq, d_rq)
            if diff_d <= 0:
                print('日期一致end，code' + code)
                continue
            k_lines = k_util_v1.get_day_k_size(code, diff_d + 1)
            k_len = len(k_lines)
            if k_len == 0:
                continue
            for i in range(k_len):
                if date_util.str_to_date(k_lines[i]['rq'], '%Y-%m-%d').date() == o_rq:
                    # 更新当天数据
                    stock_day_k.update(k_lines[i])
                    # 插入剩余数据
                    if i >= 1:
                        stock_day_k.insert_multiple(k_lines[0: i])
                    break


def add_day_macd(s_list):
    for s in s_list:
        code = s['code']
        day_macd = stock_day_macd.select(code)
        if len(day_macd) == 0:
            macd_util_v1.init_day_macd(code)
        else:
            o_rq = day_macd['rq']
            d_rq = date_util.str_to_date(date_util.get_date_str(), '%Y-%m-%d')
            diff_d = date_util.diff_days(o_rq, d_rq)
            if diff_d <= 0:
                print('日期一致end，code' + code)
                continue
            day_k_list = stock_day_k.select_interval(code, o_rq, d_rq)
            # 日期从小到大
            day_k_list.reverse()
            macd_up_list = []
            for day_k in day_k_list:
                k_rq = day_k['rq']
                if k_rq == o_rq:
                    continue
                macd_up_list.append(macd_util_v1.cal_macd_next_v1(day_macd, k_rq, day_k['sp']))
            stock_day_macd.insert_multiple(macd_up_list)


# 闭市刷新数据
def fresh_task_close():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    msg_util.miao_msg(miao_code, '定时任务-闭市刷新数据开始')
    s_list = stock_info.selectall()
    # 刷新日k,日macd
    fresh_task(s_list)
    # 持久化监控数据
    persistence_data()
    print(date_util.get_time_str() + ':刷新数据结束')
    msg_util.miao_msg(miao_code, '定时任务-闭市刷新数据结束')


# 刷新基金和板块信息
def fresh_fund_board_info():
    msg_util.miao_msg(miao_code, '定时任务-刷新基金和板块信息开始')
    # 刷新板块信息
    fresh_boards_info()
    # 刷新基金信息
    fresh_funds_info()
    msg_util.miao_msg(miao_code, '定时任务-刷新基金和板块信息结束')


# 刷新数据
def fresh_task_interval():
    # 检查有效时间
    if not date_util.valid_day(date_util.get_date_str()):
        return
    hour = date_util.get_date_time().hour
    if hour < 10 or hour > 15 or hour == 12:
        return
    s_list = stock_monitor.select_distinct()
    fresh_task(s_list)


# 刷新数据 日K + 日macd
def fresh_task(s_list):
    for stock in s_list:
        code = stock['code']
        print('code' + code + '开始刷新日数据')
        try:
            # 一、刷新日K
            k_line = k_util_v1.get_day_k_size(code, 1)
            if not k_line or len(k_line) == 0:
                print('code' + code + '停牌？')
                continue
            for k in k_line:
                sk = stock_day_k.select_rq_size(code, date_util.add_day_str(k.get('rq'), -1), 1)
                if sk:
                    k['zf'] = round((float(k.get('sp')) - sk[0].get('sp')) / sk[0].get('sp') * 100, 2)
                count = stock_day_k.update(k)
                if count == 0:
                    add_day_k([{'code': code}])
            # 二、刷新日macd
            for k in k_line:
                rq = k['rq']
                macd_list = stock_day_macd.select_newest_size(code, 2)
                if len(macd_list) <= 1 or date_util.date_to_str(macd_list[0]['rq'], '%Y-%m-%d') != rq:
                    add_day_macd([{'code': code}])
                    break
                next_macd = macd_util_v1.cal_macd_next_v1(macd_list[1], rq, float(k['sp']))
                count = stock_day_macd.update(next_macd)
                if count == 0:
                    add_day_macd([{'code': code}])
        except Exception as e:
            log_service.sys_error(code + "(fresh day k and macd error)" + str(e))


# 校验数据
def check_data():
    s_list = stock_info.selectall()
    count = 0
    for s in s_list:
        if count >= 5000:
            return
        code = s['code']
        o_count = len(k_util_v1.get_day_k_size(code, 12000))
        k_count = stock_day_k.select_count(code)
        macd_count = stock_day_macd.select_count(code)
        if o_count != k_count:
            print(code + '线上和线下数据不一致')
            stock_day_k.delete_interval(code, '1990-01-01')
            stock_day_macd.delete_all(code)
            count += 1
            continue
        if k_count != macd_count:
            print(code + 'k+macd数据不一致')
            stock_day_macd.delete_all(code)
            count += 1


# 校验macd
def check_macd():
    s_list = stock_info.selectall()
    count = 0
    for stock in s_list:
        code = stock['code']
        macd_list = macd_util_v1.MACD(macd_util_v1.parse_x(stock_day_k.select_all_asc(code), 'rq', 'sp'))
        macd_list.reverse()
        for i in range(0, min(10, len(macd_list))):
            data_c = macd_list[i]
            data_d = stock_day_macd.select_rq(code, data_c['rq'])
            if round(data_c['macd'], 10) != round(data_d['macd'], 10) \
                    or round(data_c['ema12'], 10) != round(data_d['ema12'], 10) \
                    or round(data_c['ema26'], 10) != round(data_d['ema26'], 10) \
                    or round(data_c['dea'], 10) != round(data_d['dea'], 10):
                count += 1
                print('error,error,error,error:' + str(count))
                print(str(data_c))
                print(str(data_d))
                stock_day_macd.delete_all(code)
                stock_day_macd.insert_multiple(macd_list)
                time.sleep(3)
                break


# 每天刷新一次
def fresh_month_info():
    if not date_util.valid_day(date_util.get_date_str()):
        return 0
    msg_util.miao_msg(miao_code, '定时任务-刷新月K和MACD开始')
    s_list = stock_info.selectall()
    # k
    for stock in s_list:
        code = stock['code']
        print(code + 'begin fresh month info')
        mk = stock_month_k.select(code)
        # mmacd = stock_month_macd.select(code)
        if not mk:
            k_util_v1.init_month_k(code)
            continue
        mkm = mk['rq'].month
        mkd = mk['rq'].day
        lvr = date_util.find_last_valid_day()
        lvm = lvr[5:7]
        lvd = lvr[-2:]
        if mkm != int(lvm):
            # 计算上一个月K
            dm = date_util.date_to_str(mk['rq'], '%Y-%m-%d')[0:8]
            mb = dm + '01'
            me = lvr[0:8] + '01'
            dks = stock_day_k.select_interval(code, mb, me)
            if dks:
                dkl = len(dks)
                mk['kp'] = dks[dkl - 1]['kp']
                mk['rq'] = dks[0]['rq']
                mk['sp'] = dks[0]['sp']
                for dk in dks:
                    mk['max'] = max(mk['max'], dk['max'])
                    mk['min'] = min(mk['min'], dk['min'])
                year_month = date_util.parse_date_str_f(dks[0]['rq'], '%Y-%m')
                stock_month_k.update_month(mk, year_month)
                # 刷新月macd
                fresh_month_macd(code, mk, year_month)
            # 增加最新一天月K
            dks = stock_day_k.select_interval(code, me, date_util.add_day_str(lvr, 1))
            if dks:
                dkl = len(dks)
                mk['kp'] = dks[dkl - 1]['kp']
                mk['rq'] = dks[0]['rq']
                mk['sp'] = dks[0]['sp']
                for dk in dks:
                    mk['max'] = max(mk['max'], dk['max'])
                    mk['min'] = min(mk['min'], dk['min'])
                year_month = date_util.parse_date_str_f(dks[0]['rq'], '%Y-%m')
                stock_month_k.insert(mk)
                # 刷新月macd
                fresh_month_macd(code, mk, year_month)
        else:
            if mkd == int(lvd) or date_util.get_date_time().hour < 15:
                print(code + '同一天执行任务跳过或者当天交易未结束')
                continue
            dks = stock_day_k.select_interval(code, lvr[0:8] + '01', date_util.add_day_str(lvr, 1))
            if dks:
                dkl = len(dks)
                mk['kp'] = dks[dkl - 1]['kp']
                mk['rq'] = dks[0]['rq']
                mk['sp'] = dks[0]['sp']
                for dk in dks:
                    mk['max'] = max(mk['max'], dk['max'])
                    mk['min'] = min(mk['min'], dk['min'])
                year_month = date_util.parse_date_str_f(dks[0]['rq'], '%Y-%m')
                stock_month_k.update_month(mk, year_month)
                # 处理月macd
                fresh_month_macd(code, mk, year_month)
    msg_util.miao_msg(miao_code, '定时任务-刷新月K和MACD结束')


# 处理月macd
def fresh_month_macd(code, mk, year_month):
    mkr = mk['rq']
    mmacds = stock_month_macd.select_interval(code, mkr, 2)
    if not mmacds:
        macd_util_v1.init_month_macd(code)
        return
    if len(mmacds) < 2:
        print(code + ':长度小于2')
        return
    now_macd = mmacds[0]
    pre_macd = mmacds[1]
    if now_macd['rq'].month == mk['rq'].month:
        macd_info = macd_util_v1.cal_macd_next_v1(pre_macd, mk['rq'], mk['sp'])
        stock_month_macd.update_month(macd_info, year_month)
    elif date_util.diff_days(now_macd['rq'], mkr) > 0:
        macd_info = macd_util_v1.cal_macd_next_v1(now_macd, mk['rq'], mk['sp'])
        stock_month_macd.insert(macd_info)
    else:
        log_service.sys_error(code + "(处理月macd+错误的数据注意)")


# 持久化监控数据
def persistence_data():
    print("persistence start")


# 刷新市值
key_dict = {'fresh_day_info': 'fresh_day_info'}


def fresh_stock_val():
    s_list = stock_info.selectall()
    for s in s_list:
        try:
            fresh_day_info_and_val(s)
        except Exception as e:
            log_service.sys_error(s['code'] + "(fresh day info error)" + str(e))


# 刷新
def fresh_day_info_and_val(s):
    code = s['code']
    if 0 < s['total_val'] <= 10:
        return
    print(code + 'start')
    date_str = date_util.get_date_str()
    flow = caiptal_util.get_flow(code)
    stock_info.update_the_val(code, int(flow.market_value / 100000000), int(flow.circulation_value / 100000000))
    sdi_list = stock_day_info.select_size(code, 10)
    if len(sdi_list) != 0 and \
            date_util.compare_date_str(date_util.parse_date_str(sdi_list[0]['rq']), date_str) == 0:
        sdi_list[0] = parse_util.flow_to_stock_day_info(s, flow, date_str)
        stock_day_info.update_code_rq(sdi_list[0])
    else:
        day_info = parse_util.flow_to_stock_day_info(s, flow, date_str)
        sdi_list = [day_info] + sdi_list
        stock_day_info.insert(day_info)
    # 更新板块中资金流信息
    sum_io = 0
    main_io_list = []
    for sdi in sdi_list:
        sum_io += sdi['main_io']
        main_io_list.append(sum_io)
    if sum_io == 0:
        return
    boards_info.update_stock_info(code, str({'main_io_list': main_io_list}))


def fresh_boards_info():
    print("开始刷新板块")
    gapi_v1.get_all_paltes_info()
    print("结束刷新板块")


def fresh_funds_info():
    print("开始刷新基金")
    ggfi_v1.get_info()
    print("结束刷新基金")


# 刷新日信息
def fresh_day_info():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    msg_util.miao_msg(miao_code, '定时任务-刷新日信息开始')
    # 刷新市值
    fresh_stock_val()
    msg_util.miao_msg(miao_code, '定时任务-刷新日信息结束')


# 刷新诊断信息
def fresh_diagnosis_info():
    if not date_util.valid_day(date_util.get_date_str()):
        return
    msg_util.miao_msg(miao_code, '定时任务-刷新诊断信息开始')
    dok.persistence_diagnosis([])
    msg_util.miao_msg(miao_code, '定时任务-刷新诊断信息结束')


# 刷新缓存
def fresh_cache():
    cache_util.check_expire()


# 刷新板块资金流
def fresh_board_info():
    s_list = stock_info.selectall()
    for s in s_list:
        code = s['code']
        if s['total_val'] <= 10:
            continue
        sdi_list = stock_day_info.select_size(code, 10)
        # 更新板块中资金流信息
        sum_io = 0
        main_io_list = []
        for sdi in sdi_list:
            sum_io += sdi['main_io']
            main_io_list.append(sum_io)
        if sum_io == 0:
            continue
        boards_info.update_stock_info(code, str({'main_io_list': main_io_list}))


if __name__ == '__main__':
    print('start')
    # fresh_month_info()
    # fresh_stock_val()
    # msg_util.miao_msg(miao_code, '定时任务-刷新诊断信息开始')
    # fresh_task([{'code': '600031'}])
    # fresh_stock_val()
    fresh_stock_val()
    # fresh_board_info()
    print('end')
